package com.dmz.etc.server.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.ftp.Ftp;
import com.dmz.etc.server.config.DmzProp;
import com.dmz.etc.server.config.EnvConfig;
import com.dmz.etc.server.config.enums.AnsCode;
import com.dmz.etc.server.config.enums.MsgType;
import com.dmz.etc.server.domain.MsgClazz;
import com.dmz.etc.server.domain.MsgHeader;
import com.dmz.etc.server.netty.attr.ChannelAttrs;
import com.dmz.etc.server.service.ServerService;
import com.dmz.etc.server.utils.Convert;
import com.dmz.etc.server.utils.etc.DateTools;
import com.dmz.etc.server.utils.etc.PackTool;
import com.dmz.etc.server.utils.ftp.FtpClient;
import com.dmz.etc.server.utils.ftp.FtpFile;
import com.dmz.etc.server.utils.ftp.FtpUtil;
import com.dmz.etc.server.utils.secure.CryptoUtil;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;


/**
 * @author lee
 */
@Service
@Slf4j
public class ServerServiceImpl implements ServerService {

    @Resource
    private PackTool packTool;

    @Resource
    private DmzProp dmzProp;

    @Override
    public MsgClazz decrypt(byte[] req, Channel c) throws Exception {
        String encoding = dmzProp.getDefaultEncoding();
        MsgHeader mh = new MsgHeader();
        MsgClazz msgClazz = new MsgClazz();
        byte[] decBodyByte;
        byte[] resHead = new byte[58];
        byte[] res;
        if (req != null && req.length > 0) {
            System.arraycopy(req, 0, resHead, 0, 58);

            /*int forward = c.attr(ChannelAttrs.forward).get();
            if(forward == DmzConfig.FORWARD_TO_BANK) {
                mh.analyze(resHead);
            }else {
                mh.forward(resHead);
            }*/

            mh.analyze(resHead);
            log.info("5.3开始解析报文体=================================");

            byte[] resBody = new byte[req.length - 58];
            System.arraycopy(req, 58, resBody, 0, req.length - 58);
            log.info("原始报文体内容:{}", resBody);
            log.info("5.3.1解密报文体===============================");
            if ("1001".equals(mh.getMessageType()) || "1002".equals(mh.getMessageType())) {
                log.info("收到的报文类型为:{},解密密钥为原始密钥:{}", mh.getMessageType(), packTool.getKey());
                log.info("原始密钥KeyBytes:{}", packTool.getKeyBytes());
                decBodyByte = CryptoUtil.decrypt(resBody, packTool.getKeyBytes());
                log.info("解密后的报文体:{}", decBodyByte);
                if ("1002".equals(mh.getMessageType())) {
                    //存储密钥
                    log.info("收到的报文类型为1002,解密后的报文体为动态密钥");
                    String decBody = new String(decBodyByte, encoding);
                    log.info("5.3.2.1存储动态密钥================================");
                    log.info("动态密钥:{}", decBody);
                    String key = decBody.substring(16);
                    byte[] dynamicKey = CryptoUtil.getKey(key.getBytes());
                    log.info("动态密钥KeyBytes:{}", dynamicKey);
                    c.attr(ChannelAttrs.dynamicKey).set(dynamicKey);
                    //ChannelKeyMap.addKey(id, dynamicKey);
                }
            } else {
                /*log.info("keyBytes:{}", new String(Objects.requireNonNull(ChannelKeyMap.getKey(id))));
                decBodyByte = CryptoUtil.decrypt(resBody, ChannelKeyMap.getKey(id));*/
                byte[] dynamicKey = c.attr(ChannelAttrs.dynamicKey).get();
                log.info("收到的报文类型为:{},解密密钥为动态密钥:{}", mh.getMessageType(), dynamicKey);
                decBodyByte = CryptoUtil.decrypt(resBody, dynamicKey);
                log.info("解密后的报文体:{}", decBodyByte);
                log.info("解密后的报文体转为String:{}", new String(decBodyByte, encoding));
            }
            log.info("解密后的报文体长度:{}", decBodyByte.length);
            log.info("5.4开始重组报文==============================================");
            res = new byte[decBodyByte.length + 58];
            System.arraycopy(resHead, 0, res, 0, resHead.length);
            System.arraycopy(decBodyByte, 0, res, resHead.length, res.length - resHead.length);
            msgClazz.setData(res);
            msgClazz.setBody(decBodyByte);
            msgClazz.setHead(resHead);
            msgClazz.setHeader(mh);
            msgClazz.setBodyStr(new String(decBodyByte, encoding));
            int ml = res.length;
            String bml = StrUtil.padPre(String.valueOf(ml - 4), 4, "0");
            msgClazz.setDataStr(bml + msgClazz.getHeader().toString().substring(4) + msgClazz.getBodyStr());
            log.info("重组后的报文转为字符串:{}", msgClazz.getDataStr());
            log.info("重组后的报文:{}", msgClazz.getDataStr());
        } else {
            log.info("接收到的报文为空===================================");
        }
        return msgClazz;
    }


    @Override
    public MsgClazz forward(byte[] req, Channel c) throws Exception {
        MsgClazz msgClazz = decrypt(req, c);
        MsgHeader mh = msgClazz.getHeader();
        log.info("6开始处理报文=================================");
        StringBuilder responseBody = new StringBuilder();
        byte[] reqBodyBytes = msgClazz.getBody();

        if ("1001".equals(mh.getMessageType())) {
            log.info("收到的报文为:{},处理方式：生成1002报文（动态密钥）加密回复给ETC", mh.getMessageType());
            handleMessageType1001(msgClazz, responseBody, c);
        } else if ("1003".equals(mh.getMessageType())) {
            log.info("收到的报文为:{},处理方式：生成1004签到应答报文加密回复给ETC", mh.getMessageType());
            handleMessageType1003(msgClazz, responseBody, c);
        } else {
            if ("4041".equals(mh.getMessageType())) {
                log.info("收到的报文为:{},处理方式：1.转发给CSP，2.下载并上传文件", mh.getMessageType());
                handleMessageType4041(msgClazz, reqBodyBytes);
            }
            // 其他消息类型通用的转发相关设置
            log.info("收到的报文为:{},处理方式：通用转发", mh.getMessageType());
            msgClazz.setToc(Boolean.TRUE);
            log.info("转发报文内容:{}", msgClazz.getHeader().toString() + new String(msgClazz.getBody(), dmzProp.getDefaultEncoding()));
        }
        log.info("转发或回复长度为{}的报文内容:{}", msgClazz.getData().length, msgClazz.getData());

        return msgClazz;
    }

    // 处理消息类型为1001的业务逻辑
    private void handleMessageType1001(MsgClazz msgClazz, StringBuilder responseBody, Channel c) {
        try {
            log.info("处理消息类型为1001的报文");
            responseBody.append(DateTools.getDateTimeString());
            String key = RandomUtil.randomNumbers(16);
            log.info("生成动态密钥：{}", key);
            byte[] keyBytes = Convert.hex2Bytes(Convert.str2Hex(key));
            byte[] dynamicKey = CryptoUtil.getKey(keyBytes);
            log.info("动态密钥dynamicKey:{}", Arrays.toString(dynamicKey));
            c.attr(ChannelAttrs.dynamicKey).set(dynamicKey);
            responseBody.append(AnsCode.CODE_00.getCode()).append(key);

            msgClazz.getHeader().setMessageType(MsgType.TYPE_1002.getType());
            log.info("生成报文头:{}", msgClazz.getHeader());
            byte[] res = packTool.packSet(responseBody.toString(), packTool.getKeyBytes(), msgClazz.getHeader());
            msgClazz.setData(res);
            msgClazz.setToc(Boolean.FALSE);
        } catch (Exception e) {
            log.error("处理消息类型1001出现异常", e);
            responseBody.append(AnsCode.CODE_30.getCode());
            // 可以考虑根据业务需求，在这里对msgClazz做进一步处理，比如设置错误状态等
        }
    }

    // 处理消息类型为1003的业务逻辑
    private void handleMessageType1003(MsgClazz msgClazz, StringBuilder responseBody, Channel c) throws UnsupportedEncodingException {
        responseBody.append(DateTools.getDateTimeString());
        byte[] reqBody = msgClazz.getBody();
        byte[] bizBytes = new byte[17];
        System.arraycopy(reqBody, 14, bizBytes, 0, 17);
        responseBody.append(AnsCode.CODE_00.getCode()).append(new String(bizBytes, dmzProp.getDefaultEncoding())).append("000000000000000000000000000000");
        msgClazz.getHeader().setMessageType("1004");

        try {
            byte[] res = packTool.packSet(responseBody.toString(), c.attr(ChannelAttrs.dynamicKey).get(), msgClazz.getHeader());
            msgClazz.setToc(Boolean.FALSE);
            msgClazz.setData(res);
            log.info("1004报文:{}", responseBody);
        } catch (Exception e) {
            log.info("处理消息类型1003出现异常", e);
            // 同样可根据业务需要对msgClazz做异常处理相关设置
            log.error(e.getMessage(), e);
        }
    }

    // 处理消息类型为4041的业务逻辑
    private void handleMessageType4041(MsgClazz msgClazz, byte[] reqBodyBytes) throws Exception {
        byte[] fileDateBytes = new byte[8];
        System.arraycopy(reqBodyBytes, 0, fileDateBytes, 0, 8);
        String fileDate = new String(fileDateBytes, dmzProp.getDefaultEncoding());

        byte[] fileNameBytes = new byte[36];
        System.arraycopy(reqBodyBytes, 46, fileNameBytes, 0, 36);
        String fileName = new String(fileNameBytes, dmzProp.getDefaultEncoding()).trim();
        String year = fileDate.substring(0, 4);
        String etcPath = EnvConfig.getProperty("dmz.ftp.file.path.etc.upload") + year + File.separator + fileName;
        log.info("文件远程地址{}", etcPath);
        String localPath = EnvConfig.getProperty("dmz.ftp.file.path.temp");
        log.info("文件本地临时地址{}", localPath);
        try{
            if (!FileUtil.exist(localPath + fileName)
                    || FileUtil.isEmpty(FileUtil.file(localPath + fileName))) {
                log.info("判断文件在本地不存在:{}/{}", localPath, fileName);
                log.info("从远程FTP下载文件:{}", etcPath);
                FtpClient ftpClient = new FtpClient();
                Ftp etcFtp = ftpClient.etcFtp();
                try {
                    etcFtp.download(etcPath, FileUtil.file(localPath));
                    log.info("文件下载成功！{}", fileName);
                }catch (Exception e){
                    throw new Exception(e.getMessage());
                }finally {
                    etcFtp.close();
                }
                boolean b = FileUtil.exist(localPath + fileName)
                        && !FileUtil.isEmpty(FileUtil.file(localPath + fileName));
                if(b) {
                    log.info("验证本地存在该文件！{}", fileName);

                    Ftp bankFtp = ftpClient.bankFtp();
                    String bankPath = EnvConfig.getProperty("dmz.ftp.file.path.bank");
                    FtpFile serverFile = new FtpFile(bankFtp, bankPath, localPath + fileName, fileName);
                    log.info("将本地文件上传到远程FTP:{}", bankPath);
                    FtpUtil.upload(serverFile);
                    log.info("上传文件成功{}", serverFile.getFileName());
                    FtpUtil.close(etcFtp);
                    FileUtil.del(localPath + fileName);
                    log.info("删除临时文件成功{}", localPath + fileName);
                }else {
                    log.info("下载文件为空或远程文件不存在");
                }
            }else {
                log.info("判断本地文件存在===一般不会走到这里除非对方重复发送");
            }
        } catch (Exception e) {
            log.error("4041文件传输异常", e);
            throw e;
            // 根据业务决定是否要对msgClazz等做额外处理来表示异常情况
        }
    }

    @Override
    public MsgClazz encrypt(byte[] req, Channel c) throws Exception {

        String encoding = dmzProp.getDefaultEncoding();
        log.info("接收到的原始报文:{}", Arrays.toString(req));
        log.info("接收到的报文转字符串:{}", new String(req, encoding));
        MsgHeader mh = new MsgHeader();
        MsgClazz msgClazz = new MsgClazz();
        byte[] encBodyByte;
        if (req != null && req.length > 0) {
            //mh.forward(req.substring(0,58));
            byte[] resHead = new byte[58];
            System.arraycopy(req, 0, resHead, 0, 58);
            log.info("原始报文头:{}", Arrays.toString(req));
            mh.analyze(resHead);
            byte[] totalLenByte = new byte[4];
            System.arraycopy(resHead, 0, totalLenByte, 0, 4);
            byte[] messageIdByte = new byte[4];
            System.arraycopy(resHead, 22, messageIdByte, 0, 4);
            String totalLen = new String(totalLenByte, encoding);
            String messageId = new String(messageIdByte, encoding);
            totalLenByte = Convert.int2Bytes(Integer.parseInt(totalLen) + 4);
            messageIdByte = Convert.int2Bytes(Integer.parseInt(messageId));
            byte[] reqBodyBytes = new byte[req.length - 58];
            log.info("6处理报文 处理方式：对报文体加密====================================");
            log.info("对报文体加密:{}", Arrays.toString(reqBodyBytes));
            System.arraycopy(req, 58, reqBodyBytes, 0, req.length - 58);
            if ("1001".equals(mh.getMessageType()) || "1002".equals(mh.getMessageType())) {
                log.info("收到的报文为:{},加密因子为原始加密因子{}", mh.getMessageType(), packTool.getKey());
                encBodyByte = CryptoUtil.encrypt(reqBodyBytes, packTool.getKeyBytes());
            } else {
                log.info("收到的报文为:{},加密因子为动态因子{}", mh.getMessageType(), new String(c.attr(ChannelAttrs.dynamicKey).get()));
                encBodyByte = CryptoUtil.encrypt(reqBodyBytes, c.attr(ChannelAttrs.dynamicKey).get());
            }

            log.info("加密后的报文体:{}", Arrays.toString(encBodyByte));
            byte[] res = new byte[resHead.length + encBodyByte.length];
            System.arraycopy(totalLenByte, 0, res, 0, 4);
            System.arraycopy(resHead, 4, res, 4, 22);
            System.arraycopy(messageIdByte, 0, res, 22, 4);
            System.arraycopy(resHead, 0, res, 26, 32);
            System.arraycopy(encBodyByte, 0, res, resHead.length, encBodyByte.length);
            msgClazz.setData(res);
            log.info("加密后重组的报文:{}", Arrays.toString(req));
            msgClazz.setToc(Boolean.TRUE);
        } else {
            log.error("fallback request is null");
            msgClazz.setToc(Boolean.FALSE);
        }
        return msgClazz;
    }

}
